<?php

class Kd_Data_Table {

    private $_name = NULL;
    private $_updaterClass = NULL;
    private $_idColumn = NULL;
    private $_columns = array();
    private $_updater = NULL;
    private $_connectionName = NULL;

    public function __construct() {

    }

    public function getDbDriver($transaction = NULL) {
        return is_null($transaction) ? Kd_Db_Factory::getDriver($this->getConnectionName()) : $transaction->getDbDriver();
    }

    public function createColumn($columnName, $columnType = 'string', $primaryKey = FALSE, $increment = FALSE) {
        $class = 'Kd_Data_Table_Column_'.ucfirst($columnType);
        $result = new $class();
        $result->setColumnName($columnName)
                ->setIsPrimaryKey($primaryKey)
                ->setIsIncrement($increment);
        $this->_columns[$columnName] = $result;
        return $result;
    }

    public function getConnectionName() {
        return $this->_connectionName;
    }

    protected function setConnectionName($value) {
        $this->_connectionName = $value;
        return $this;
    }

    public function getName() {
        return $this->_name;
    }

    public function setName($value) {
        $this->_name = $value;
        return $this;
    }

    public function setUpdaterClass($value) {
        $this->_updaterClass = $value;
        $this->_updater = NULL;
        return $this;
    }

    public function hasColumn($name) {
        return array_key_exists($name, $this->getColumns());
    }

    public function getColumns() {
        return $this->_columns;
    }

    public function getColumn($name) {
        if (isset($this->_columns[$name])) {
            return $this->_columns[$name];
        }
        return NULL;
    }

    public function getIdColumn() {
        if (is_null($this->_idColumn)) {
            foreach ($this->getColumns() as $name => $column) {
                if ($column->getIsPrimaryKey()) {
                    $this->_idColumn = $column;
                    break;
                }
            }
        }
        return $this->_idColumn;
    }

    public function getInsertId($transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        return $driver->getInsertId();
    }

    public function insert(array $values, $transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        $insert = new Kd_Data_Dcl_Insert();
        foreach ($this->getColumns() as $name => $column) {
            if ($column->getInsertable()) {
                $insert->value($name, $column->getParameterName());
                if (array_key_exists($name, $values)) {
                    $column->validate($values[$name]) ?
                                    $insert->addParameter($name, $column->convertToDb($values[$name])) :
                                    $insert->addParameter($name, $column->getDefaultValue());
                } else {
                    $insert->addParameter($name, $column->getDefaultValue());
                }
            }
        }
        return $insert->into($this->getName())
                ->setDbDriver($driver)
                ->execute();
    }

    public function update(array $values, $transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        $updateValues = array();
        foreach ($this->getColumns() as $name => $column) {
            if ($column->getUpdateable() && array_key_exists($name, $values)) {
                $updateValues[$name] = $column->validate($values[$name]) ?
                        array('arg' => $column->getParameterName(), 'val' => $column->ConvertToDb($values[$name])) :
                        array('arg' => $column->getParameterName(), 'val' => $column->getDefaultValue());
            }
        }

        if (!empty($updateValues)) {
            $idColumn = $this->getIdColumn();
            $update = new Kd_Data_Dcl_Update();
            foreach ($updateValues as $name => $argVal) {
                $update->value($name, $argVal['arg']);
                $update->addParameter($name, $argVal['val']);
            }

            return $update->into($this->getName())
                    ->where()
                    ->eq($idColumn->getColumnName(), $idColumn->getParameterName())
                    ->closest()
                    ->addParameter($idColumn->getColumnName(), $values[$idColumn->getColumnName()])
                    ->setDbDriver($driver)
                    ->execute();
        }
        return 0;
    }

    public function delete($id, $transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        $idColumn = $this->getIdColumn();
        $delete = new Kd_Data_Dcl_Delete();
        return $delete->from($this->getName())
                ->where()
                ->eq($idColumn->getColumnName(), $idColumn->getParameterName())
                ->closest()
                ->addParameter($idColumn->getColumnName(), $id)
                ->setDbDriver($driver)
                ->execute();
    }

    public function updateBatch(array $ids, array $values, $transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        $update = new Kd_Data_Dcl_Update();
        foreach ($this->getColumns() as $name => $column) {
            if (array_key_exists($name, $values)) {
                $update->value($name, $column->getParameterName());
                $update->addParameter($name, $values[$name]);
            }
        }

        $idColumn = $this->getIdColumn();
        return $update->into($this->getName())
                ->where()
                ->contain($idColumn->getColumnName(), $idColumn->getParameterName())
                ->closest()
                ->addParameter($idColumn->getColumnName(), $ids)
                ->setDbDriver($driver)
                ->execute();
    }

    public function deleteBatch(array $ids, $transaction = NULL) {
        $driver = $this->getDbDriver($transaction);
        $idColumn = $this->getIdColumn();
        $delete = new Kd_Data_Dcl_Delete();
        return $delete->from($this->getName())
                ->where()
                ->contain($idColumn->getColumnName(), $idColumn->getParameterName())
                ->closest()
                ->setDbDriver($driver)
                ->addParameter($idColumn->getColumnName(), $ids)
                ->execute();
    }

    public function checkExists(array $conditions) {
        $query = Kd_Data_Factory::getQueryDcl()->from($this->getName());
        $query->field(Kd_Data_Dcl_Fn::count(), 'records');
        foreach ($conditions as $name => $value) {
            $column = $this->getColumn($name);
            $query->eq($name, $column->getParameterName());
        }
        $query->setParameters($conditions);
        return $query->fetch()->getFirstValue() > 0;
    }

    public function getById($id) {
        $idColumn = $this->getIdColumn();
        return Kd_Data_Factory::getQueryDcl()
                ->from($this->getName())
                ->eq($idColumn->getColumnName(), $idColumn->getParameterName())
                ->addParameter($idColumn->getColumnName(), $id)
                ->fetch()
                ->getFirstRow();
    }

//    public function getAll() {
//        return Kd_Data_Factory::getQueryDcl()
//                ->from($this->getName())
//                ->fetch();
//    }
}